﻿#pragma once

#include <learn/geom/vec.h>

namespace xi
{

namespace alg
{

struct Alg_White_Noise
{
    static void generate(int row, int col, double *whiteNoise)
    {
        std::mt19937 generator(std::random_device{}());
        std::uniform_real_distribution<double> uniform(0, 1);

        for (int i = 0; i < row; i++)
            for (int j = 0; j < col; j++)
                whiteNoise[i * col + j] = uniform(generator);
    }
};

struct Alg_Smooth_Noise
{
    static void generate(int row, int col, double *baseNoise, int octave, double *smoothNoise)
    {
        // 采样周期和频率
        int samplePeriod = 1 << octave; // calculates 2 ^ k
        double sampleFrequency = 1.0f / samplePeriod;

        // 线性插值
        auto inerp = [](double x, double y, double blend) { return x * (1 - blend) + y * blend; };

        for (int i = 0; i < row; i++)
        {
            // 水平采样索引
            int sample_i0 = (i / samplePeriod) * samplePeriod;
            int sample_i1 = (sample_i0 + samplePeriod) % row; // wrap around
            double horizontal_blend = (i - sample_i0) * sampleFrequency;

            for (int j = 0; j < col; j++)
            {
                // 垂直采样索引
                int sample_j0 = (j / samplePeriod) * samplePeriod;
                int sample_j1 = (sample_j0 + samplePeriod) % col; // wrap around
                double vertical_blend = (j - sample_j0) * sampleFrequency;

                double top = inerp(baseNoise[sample_i0 * col + sample_j0], baseNoise[sample_i1 * col + sample_j0],
                                   horizontal_blend);
                double bottom = inerp(baseNoise[sample_i0 * col + sample_j1], baseNoise[sample_i1 * col + sample_j1],
                                      horizontal_blend);
                smoothNoise[i * col + j] = inerp(top, bottom, vertical_blend);
            }
        }
    }
};

struct Alg_Perlin_Noise
{
    static void generate(int row, int col, int octaveCount, double *perlinNoise)
    {
        // 本底噪声
        std::vector<double> baseNoise(row * col);
        Alg_White_Noise::generate(row, col, baseNoise.data());

        // 平滑噪声
        std::vector<std::vector<double>> smoothNoise(octaveCount, std::vector<double>(row * col));
        for (int i = 0; i < octaveCount; i++)
            Alg_Smooth_Noise::generate(row, col, baseNoise.data(), i, smoothNoise[i].data());

        double totalAmplitude = 0.0;
        double amplitude = 1.0;
        double persistance = 0.5;

        // 混合噪声
        for (int octave = octaveCount - 1; octave >= 0; octave--)
        {
            // 振幅指数递减
            amplitude *= persistance;
            totalAmplitude += amplitude;

            for (int i = 0; i < row; i++)
                for (int j = 0; j < col; j++)
                    perlinNoise[i * col + j] += smoothNoise[octave][i * col + j] * amplitude;
        }

        // 归一化
        for (int i = 0; i < row; i++)
            for (int j = 0; j < col; j++)
                perlinNoise[i * col + j] /= totalAmplitude;
    }
};

} // namespace alg

} // namespace xi
